﻿using ogc = GeoAPI.Geometries;
using System.Windows.Media;
using DeepEarth.Map.Core;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Controls;
using GisSharpBlog.NetTopologySuite.IO;
using GisSharpBlog.NetTopologySuite.Geometries;
using System;
using DeepEarth.Core;

namespace DeepEarth.Toolkit.Geometry.OGC
{
    internal class GeometryWrapper
    {
        public ogc.IGeometry OgcGeometry { get; set; }
        public List<List<Location>> LocationCollections { get; set; }
        public Location Point { get; set; }
        public UIElement View { get; set; }
    }


    public class OgcGeometryItems
    {
        // ogc geometry collections are stored as children
        List<OgcGeometryItems> Children = new List<OgcGeometryItems>();

        // non geometry collections are stored as plain wrappers
        List<GeometryWrapper> GeometryWrappers = new List<GeometryWrapper>();

        // the wrapper corresponding to the current geometry
        GeometryWrapper GeometryWrapper;

        public void Process(Func<Location, System.Windows.Point> locationConverterFunction, ogc.IGeometry ogcGeometry)
        {
            if (ogcGeometry.GeometryType.ToUpper() != GeometryType.GeometryCollection)
            {
                this.GeometryWrapper = CreateWrapper(locationConverterFunction, ogcGeometry);
            }
            else
            {
                ogc.IGeometryCollection multiGeometry = ogcGeometry as ogc.IGeometryCollection;
                foreach (ogc.IGeometry childGeometry in multiGeometry.Geometries)
                {
                    if (childGeometry.GeometryType.ToUpper() != GeometryType.GeometryCollection)
                    {
                        GeometryWrapper childWrapper = CreateWrapper(locationConverterFunction, childGeometry);
                        this.GeometryWrappers.Add(childWrapper);

                    }
                    else
                    {
                        OgcGeometryItems geometryItems = new OgcGeometryItems();
                        this.Children.Add(geometryItems);
                        geometryItems.Process(locationConverterFunction, childGeometry);
                    }
                }
            }
        }

        internal GeometryWrapper CreateWrapper(Func<Location, System.Windows.Point> locationConverterFunction, ogc.IGeometry ogcGeometry)
        {
            GeometryWrapper wrapper = new GeometryWrapper
            {
                OgcGeometry = ogcGeometry
            };

            switch (ogcGeometry.GeometryType.ToUpper())
            {
                case GeometryType.Point:
                    wrapper.Point = GeometryUtils.GetLocation(ogcGeometry.Coordinate);
                    wrapper.View = new Pushpin { Position = wrapper.Point };
                    break;

                case GeometryType.LineString:
                    {
                        wrapper.LocationCollections = new List<List<Location>>();
                        wrapper.LocationCollections.Add(GeometryUtils.GetLocations(ogcGeometry));

                        System.Windows.Media.Geometry geometry = GeometryUtils.GetFrameworkGeometry(locationConverterFunction, ogcGeometry);
                        Path pathShape = new Path();
                        pathShape.Data = geometry;
                        wrapper.View = pathShape;
                    }
                    break;

                case GeometryType.Polygon:
                    {
                        wrapper.LocationCollections = new List<List<Location>>();
                        ogc.IPolygon polygon = ogcGeometry as ogc.IPolygon;
                        wrapper.LocationCollections.Add(GeometryUtils.GetLocations(polygon.Shell));

                        foreach (ogc.ILinearRing inner in polygon.InteriorRings)
                        {
                            wrapper.LocationCollections.Add(GeometryUtils.GetLocations(inner));
                        }
                        System.Windows.Media.Geometry geometry = GeometryUtils.GetFrameworkGeometry(locationConverterFunction, ogcGeometry);
                        Path pathShape = new Path();
                        pathShape.Data = geometry;
                        wrapper.View = pathShape;
                    }

                    break;

                case GeometryType.MultiLineString:
                    {
                        wrapper.LocationCollections = new List<List<Location>>();
                        ogc.IMultiLineString mls = ogcGeometry as ogc.IMultiLineString;

                        foreach (ogc.ILineString line in mls.Geometries)
                        {
                            wrapper.LocationCollections.Add(GeometryUtils.GetLocations(line));
                        }
                        System.Windows.Media.Geometry geometry = GeometryUtils.GetFrameworkGeometry(locationConverterFunction, ogcGeometry);
                        Path pathShape = new Path();
                        pathShape.Data = geometry;
                        wrapper.View = pathShape;
                    }

                    break;

                case GeometryType.MultiPolygon:
                    {
                        wrapper.LocationCollections = new List<List<Location>>();
                        wrapper.LocationCollections.Add(GeometryUtils.GetLocations(ogcGeometry));
                        System.Windows.Media.Geometry geometry = GeometryUtils.GetFrameworkGeometry(locationConverterFunction, ogcGeometry);
                        Path pathShape = new Path();
                        pathShape.Data = geometry;
                        wrapper.View = pathShape;
                    }
                    break;
            }

            return wrapper;
        }

        public List<UIElement> GetVisualElements()
        {
            List<UIElement> elements = new List<UIElement>();

            if (this.GeometryWrapper != null && this.GeometryWrapper.View != null)
            {
                elements.Add(this.GeometryWrapper.View);
            }

            foreach (GeometryWrapper wrapper in this.GeometryWrappers)
            {
                if (wrapper.View != null)
                {
                    elements.Add(wrapper.View);
                }
            }

            foreach (OgcGeometryItems ogi in Children)
            {
                elements.AddRange(ogi.GetVisualElements());
            }

            return elements;
        }

    }
}
